package com.zyk.scaffold.oauth.config.oauth.grant;

import com.zyk.scaffold.core.component.CountRedisLimitComponent;
import com.zyk.scaffold.oauth.component.CaptchaComponent;
import com.zyk.scaffold.oauth.component.TenantCodeCachComponent;
import com.zyk.scaffold.oauth.config.oauth.support.LocalUserDetailsService;
import com.zyk.scaffold.oauth.framework.domain.ClientDetails;
import com.zyk.scaffold.oauth.framework.domain.OauthContext;
import com.zyk.scaffold.oauth.framework.domain.OauthRequest;
import com.zyk.scaffold.oauth.framework.domain.UserDetails;
import com.zyk.scaffold.oauth.framework.interfaces.IClientDetailsService;
import com.zyk.scaffold.oauth.framework.interfaces.ITokenGranter;
import com.zyk.scaffold.core.component.PasswordEncoderComponent;
import com.zyk.scaffold.common.utils.AssertUtil;
import com.zyk.scaffold.common.utils.MapUtils;
import com.zyk.scaffold.common.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;

import java.security.NoSuchAlgorithmException;

@Slf4j
public class PasswordTokenGranter implements ITokenGranter {

    public static final String GRAPH_ID = "graphId";
    public static final String TENANT_CODE = "tenantCode";

    private String grantType = "password";
    private IClientDetailsService clientDetailsService;
    private LocalUserDetailsService localUserDetailsService;
    private PasswordEncoderComponent passwordEncoderComponent;
    private CaptchaComponent captchaComponent;
    private TenantCodeCachComponent tenantCodeCachComponent;
    private CountRedisLimitComponent countRedisLimitComponent;


    public PasswordTokenGranter(IClientDetailsService clientDetailsService, LocalUserDetailsService localUserDetailsService, PasswordEncoderComponent passwordEncoderComponent, CaptchaComponent captchaComponent, TenantCodeCachComponent tenantCodeCachComponent, CountRedisLimitComponent countRedisLimitComponent) {
        this.clientDetailsService = clientDetailsService;
        this.localUserDetailsService = localUserDetailsService;
        this.passwordEncoderComponent = passwordEncoderComponent;
        this.captchaComponent = captchaComponent;
        this.tenantCodeCachComponent = tenantCodeCachComponent;
        this.countRedisLimitComponent = countRedisLimitComponent;
    }

    @Override
    public OauthContext grant(OauthRequest request) {
        String clientId = request.getClientId();
        String account = request.getAccount();
        String password = request.getPassword();
        String clientSecret = request.getClientSecret();
        String tenantCode = MapUtils.getString(request.getRequestParam(), TENANT_CODE);
        if(StringUtils.isEmpty(tenantCode)){
            tenantCode = tenantCodeCachComponent.getTenantCode(account);
        }
        String limitKey = "login:" + tenantCode + ":" + account;
        AssertUtil.isTrue(countRedisLimitComponent.check(limitKey, 5),"错误超过6次, 租户下账号已自动锁定！");
        String graphId = MapUtils.getString(request.getRequestParam(), GRAPH_ID);
        AssertUtil.notEmptyString(graphId, "图形ID不能为空");
        ClientDetails clientDetails = this.clientDetailsService.loadClientDetailsByClientId(clientId);
        AssertUtil.notNull(clientDetails, "客户端不存在,禁止登录");
        AssertUtil.isTrue(clientDetails.getClientSecret().equals(clientSecret), "客户端密钥错误");
        UserDetails userDetails = this.localUserDetailsService.loadTenantUserDetails(account, tenantCode);
        AssertUtil.notNull(userDetails, "账号不存在,禁止登录");
        String captcha = captchaComponent.getCaptcha(graphId);
        AssertUtil.notEmptyString(captcha, "验证码已失效");
        log.info("password:{}, user:{}, captchaValidateCode:{}",password, userDetails.getPassword(), captcha.toUpperCase());
        try {
            boolean matches = passwordEncoderComponent.matches(password, userDetails.getPassword(), captcha.toUpperCase());
            if(!matches){
                this.countRedisLimitComponent.incrby(limitKey, 600);
                AssertUtil.error("账号或密码错误");
            }
        } catch (NoSuchAlgorithmException e) {
            this.countRedisLimitComponent.incrby(limitKey, 600);
            AssertUtil.error("账号或密码错误");
        }
        captchaComponent.removeCaptcha(graphId);
        OauthContext oauthContext = new OauthContext(userDetails, request, clientDetails);
        return oauthContext;
    }

    @Override
    public String getGrantType() {
        return grantType;
    }
}
